import json
import base64
from typing import Optional, Dict, Any
from quart import request, g
from loguru import logger as log
from src.common.utils.context_manager import RequestContext


class AuthMiddleware:
    """认证中间件"""
    
    def __init__(self, app=None):
        self.app = app
        if app is not None:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化中间件"""
        self.app = app
        app.before_request(self.before_request)
        app.after_request(self.after_request)
    
    async def before_request(self):
        """请求前处理"""
        try:
            # 获取认证相关的请求头
            blade_auth = request.headers.get('Blade-auth', '')
            project_id = request.headers.get('Project-id', '')
            tenant_id_header = request.headers.get('X-Tenant-Id', '')
            
            # 获取请求参数
            request_params = await self._get_request_params()
            
            # 初始化用户信息
            user_id = None
            user_info = None
            
            # 解析Blade-auth认证信息
            if blade_auth:
                try:
                    # 解码Base64
                    decoded_auth = base64.b64decode(blade_auth).decode('utf-8')
                    auth_data = json.loads(decoded_auth)
                    
                    # 提取用户信息
                    user_id = auth_data.get('user_id') or auth_data.get('userId')
                    user_info = auth_data.get('user_info') or auth_data.get('userInfo') or auth_data
                    
                    # 将用户信息存储到上下文
                    if user_id:
                        RequestContext.set_user_id(user_id)
                    if user_info:
                        RequestContext.set_user_info(user_info)
                    
                except Exception as e:
                    log.warning(f"Failed to decode Blade-auth: {str(e)}")
            
            # 处理租户ID
            if user_info:
                # 从用户信息中提取租户ID
                if isinstance(user_info, dict):
                    tenant_id = user_info.get('tenant_id') or user_info.get('tenantId')
                    if tenant_id:
                        RequestContext.set_tenant_id(tenant_id)
            
            # 如果JWT中没有tenant_id，尝试从请求头获取
            if not RequestContext.get_tenant_id() and tenant_id_header:
                RequestContext.set_tenant_id(tenant_id_header)
            
            # 如果没有有效的认证信息，记录警告
            if not user_info and blade_auth:
                log.warning(f"Failed to parse Blade-auth token for request {request.path}")
            
            # 将项目ID存储到上下文
            if project_id:
                RequestContext.set_project_id(project_id)
            
            # 打印请求信息
            self._log_request_info(request.path, request.method, request_params, user_id, project_id, blade_auth)
            
            # 将认证信息存储到g对象中，供路由使用
            g.user_id = user_id
            g.project_id = project_id
            g.user_info = user_info
            g.blade_auth = blade_auth
            g.tenant_id = RequestContext.get_tenant_id()
            
        except Exception as e:
            log.error(f"Auth middleware error: {str(e)}")
            # 即使认证失败，也不阻止请求继续，让具体的路由决定是否需要认证
    
    async def after_request(self, response):
        """请求后处理"""
        try:
            # 打印响应信息
            await self._log_response_info(response)
            
            # 清空上下文（可选，因为每个请求都有独立的上下文）
            # RequestContext.clear()
        except Exception as e:
            log.error(f"Auth middleware after_request error: {str(e)}")
        
        return response
    
    async def _get_request_params(self) -> Dict[str, Any]:
        """获取请求参数"""
        params = {}
        
        try:
            # 获取查询参数
            if request.args:
                params['query'] = dict(request.args)
            
            # 获取表单数据或JSON数据
            if request.method in ['POST', 'PUT', 'PATCH']:
                content_type = request.headers.get('Content-Type', '')
                
                if 'application/json' in content_type:
                    try:
                        json_data = await request.get_json()
                        if json_data:
                            params['json'] = json_data
                    except Exception:
                        pass
                elif 'application/x-www-form-urlencoded' in content_type or 'multipart/form-data' in content_type:
                    try:
                        form_data = await request.form
                        if form_data:
                            params['form'] = dict(form_data)
                    except Exception:
                        pass
        
        except Exception as e:
            log.warning(f"Failed to get request params: {str(e)}")
        
        return params
    
    def _log_request_info(self, path: str, method: str, params: Dict[str, Any], 
                         user_id: Optional[str], project_id: Optional[str], blade_auth: str):
        """打印请求信息"""
        try:
            # 构建日志信息
            log_info = {
                'path': path,
                'method': method,
                'user_id': user_id or 'anonymous',
                'project_id': project_id or 'none',
                'has_auth': bool(blade_auth),
                'params': params
            }
            
            # 格式化输出
            log_message = (
                f"[请求参数:] {method} {path} | "
                f"UserId: {user_id or 'anonymous'} | "
                f"ProjectId: {project_id or 'none'} | "
                f"HasAuth: {bool(blade_auth)} | "
                f"Params: {json.dumps(params, ensure_ascii=False, separators=(',', ':'))}"
            )
            
            log.info(log_message)
            
        except Exception as e:
            log.error(f"Failed to log request info: {str(e)}")
    
    async def _log_response_info(self, response):
        """打印响应信息"""
        try:
            # 获取响应状态码
            status_code = response.status_code
            
            # 获取响应数据
            response_data = None
            content_type = response.headers.get('Content-Type', '')
            
            # 只处理JSON响应
            if 'application/json' in content_type:
                try:
                    # 获取响应体数据
                    response_body = await response.get_data()
                    if response_body:
                        response_data = json.loads(response_body.decode('utf-8'))
                        
                        # 限制响应数据长度，避免日志过长
                        response_str = json.dumps(response_data, ensure_ascii=False, separators=(',', ':'))
                        if len(response_str) > 1000:
                            response_str = response_str[:1000] + '...'
                        response_data = response_str
                    else:
                        response_data = 'empty'
                except Exception:
                    response_data = 'parse_error'
            else:
                response_data = f'non_json({content_type})'
            
            # 获取当前用户和项目信息
            user_id = self.get_current_user_id()
            project_id = self.get_current_project_id()
            
            # 格式化输出
            log_message = (
                f"[响应参数:] {request.method} {request.path} | "
                f"UserId: {user_id or 'anonymous'} | "
                f"ProjectId: {project_id or 'none'} | "
                f"Status: {status_code} | "
                f"Response: {response_data}"
            )
            
            log.info(log_message)
            
        except Exception as e:
            log.error(f"Failed to log response info: {str(e)}")
    
    @staticmethod
    def get_current_user_id() -> Optional[str]:
        """获取当前用户ID（从上下文或g对象）"""
        # 优先从上下文获取
        user_id = RequestContext.get_user_id()
        if user_id:
            return user_id
        
        # 从g对象获取
        return getattr(g, 'user_id', None)
    
    @staticmethod
    def get_current_project_id() -> Optional[str]:
        """获取当前项目ID（从上下文或g对象）"""
        # 优先从上下文获取
        project_id = RequestContext.get_project_id()
        if project_id:
            return project_id
        
        # 从g对象获取
        return getattr(g, 'project_id', None)
    
    @staticmethod
    def get_current_user_info() -> Optional[Dict[str, Any]]:
        """获取当前用户信息（从上下文或g对象）"""
        # 优先从上下文获取
        user_info = RequestContext.get_user_info()
        if user_info:
            return user_info
        
        # 从g对象获取
        return getattr(g, 'user_info', None)
    
    @staticmethod
    def get_current_tenant_id() -> Optional[str]:
        """获取当前租户ID（从上下文或g对象）"""
        # 优先从上下文获取
        tenant_id = RequestContext.get_tenant_id()
        if tenant_id:
            return tenant_id
        
        # 从g对象获取
        return getattr(g, 'tenant_id', None)
    
    @staticmethod
    def require_auth() -> bool:
        """检查是否已认证"""
        return AuthMiddleware.get_current_user_id() is not None